package com.livestocksolutions.ui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Date;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;

import com.livestocksolutions.LivestockSolutions;
import com.livestocksolutions.Owner;
import com.livestocksolutions.UsableItem;
import com.livestocksolutions.UsableItemHistory;
import com.livestocksolutions.util.DecimalDocumentFilter;
import com.livestocksolutions.util.JDatePickerDate;
import com.livestocksolutions.util.UsableItemList;

import javax.swing.JTextField;
import java.awt.GridLayout;

/**
 * Create and show an Used Item, and allow modifying some of the contents.
 * @author Adam Parr
 * 
 * @.modified 15 March 2013
 * 
 * @param <A> The type of Object that will have the UsableItemHistory applied to.
 * @param <B> The type of the UsableItemHistory.
 * @param <C> The item type that is within the UsableItemHistory.
 */
public abstract class UsedItemFrame<A,B extends UsableItemHistory,C extends UsableItem<?>> extends JFrame {
	private static final long serialVersionUID = 1L;
	protected JButton btnCreateOrUpdate;
	protected JButton btnClear;
	protected JPanel northPanel;
	protected JPanel southPanel;
	protected JPanel panel;
	protected JPanel panel_1;
	protected JPanel panel_2;
	protected JPanel panel_3;
	protected JPanel panel_4;
	protected JPanel panel_5;
	protected JComboBox nameCB;
	protected JDatePickerDate dateField;
	protected JTextField quantity;
	protected JComboBox measurement;
	protected JTextField cost;
	protected JComboBox manuCB;
	protected JLabel titleLabel = new JLabel();
	
	/**
	 * A check to determine if the item in its current state is valid to be saved.
	 */
	protected final DocumentListener dl = new DocumentListener() {
		public void changedUpdate(DocumentEvent e) {
			doCheck();
		}
		public void removeUpdate(DocumentEvent e) {
			doCheck();
		}
		public void insertUpdate(DocumentEvent e) {
			doCheck();
		}
		
		/**
		 * Checks to see if it is OK to save the item as it currently is.<br>
		 * Disables the save button if it is not OK, enables it if it is.
		 */
		private void doCheck(){
			boolean valid=false;
			if(nameCB.getSelectedItem()!=null && dateField.getDate()!=null && !quantity.getText().trim().equals("") && measurement.getSelectedItem()!=null){
				valid=true;
			}
			valid=UsedItemFrame.this.doCheck(valid);
			btnCreateOrUpdate.setEnabled(valid);
		}
	};

	/**
	 * This class will show a Frame of each type of Used Item.
	 * @param parent The Bovine that will have this UsedItem.
	 * @param usedItemHistory The UsedItemHistory to be passed in. Pass in a <tt>null</tt> if this is a new UsedItemHistory.
	 */
	protected UsedItemFrame(A parent, B usedItemHistory){
		createFrame(parent,usedItemHistory);
	}
	
	@Override
	public void setTitle(String title){
		super.setTitle(title);
		titleLabel.setText(title);
	}
	
	/**
	 * This class will show a Frame of each type of Used Item.
	 * @param parent The object that will have this usedItemHistory.
	 * @param usedItemHistory The UsedItemHistory to be passed in. Pass in <tt>null</tt> if this is a new UsedItemHistory.
	 */
	private void createFrame(final A parent, final B usedItemHistory){
		setAlwaysOnTop(true);
		setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		setIconImage(MainFrame.getImage(MainFrame.OBSERVATION_LIST));
		
		getContentPane().setLayout(new BorderLayout(0, 5));
		
		panel_4 = new JPanel();
		getContentPane().add(panel_4, BorderLayout.NORTH);
		panel_4.setLayout(new BorderLayout(0, 0));
		
		panel_5 = new JPanel();
		panel_4.add(panel_5, BorderLayout.NORTH);
		
		panel_5.add(titleLabel);
		
		panel_1 = new JPanel();
		panel_4.add(panel_1);
		
		northPanel = new JPanel();
		panel_1.add(northPanel);
		northPanel.setBorder(new EmptyBorder(5,5,5,5));
		northPanel.setLayout(new GridLayout(0, 2, 5, 5));

		northPanel.add(new JLabel("Name:"));
		
		nameCB = new JComboBox();
		nameCB.addActionListener(new ActionListener () {
		    public void actionPerformed(ActionEvent e) {
		    	String type="";
		    	if(measurement.getSelectedItem()!=null){
		    		type=((UsableItemList<?>)nameCB.getSelectedItem()).getItem().getMeasurement();
		    	}
		        
		        String[] measurementSelect = new String[] {"ml", "l", "mg", "g", "kg", "Tablet"};
				if(type.toLowerCase().equals("ml")||type.toLowerCase().equals("l")){
					measurementSelect = new String[] {"ml", "l"};
				}else if(type.toLowerCase().equals("mg")||type.toLowerCase().equals("g")||type.toLowerCase().equals("kg")){
					measurementSelect = new String[] {"mg","g", "kg"};
				}else if(type.toLowerCase().equals("tablet")){
					measurementSelect = new String[] {"Tablet"};
				}
				Object selected = measurement.getSelectedItem();
				measurement.setModel(new DefaultComboBoxModel(measurementSelect));
				if(selected!=null){
					measurement.setSelectedItem(selected);
				}else{
					measurement.setSelectedItem(type);
				}
				if(measurement.getSelectedItem()==null){
					measurement.setSelectedItem(type);
				}
				Dimension d = measurement.getPreferredSize();
				d.setSize(100d, d.getHeight());
				measurement.setPreferredSize(d);
		    }
		});
		northPanel.add(nameCB);
		
		northPanel.add(new JLabel("Date Used:"));
		
		dateField = JDatePickerDate.createJDatePicker();
		northPanel.add(dateField);
		
		northPanel.add(new JLabel("Quantity Used:"));
		
		panel = new JPanel();
		northPanel.add(panel);
		panel.setLayout(new BorderLayout(0, 0));
		
		quantity = new JTextField();
		panel.add(quantity, BorderLayout.CENTER);
		((AbstractDocument)quantity.getDocument()).setDocumentFilter(new DecimalDocumentFilter(0d,Double.POSITIVE_INFINITY));
		quantity.setColumns(3);
		
		measurement = new JComboBox();
		panel.add(measurement, BorderLayout.EAST);
		
		panel_2 = new JPanel();
		getContentPane().add(panel_2, BorderLayout.CENTER);
		panel_2.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
		
		createSecondaryValues(parent, usedItemHistory);
				
		panel_3 = new JPanel();
		getContentPane().add(panel_3, BorderLayout.SOUTH);
		panel_3.setLayout(new BorderLayout(0, 0));

		JSeparator separator = new JSeparator();
		panel_3.add(separator, BorderLayout.NORTH);
		btnCreateOrUpdate = new JButton(usedItemHistory==null?"Create":"Save");
		
		btnCreateOrUpdate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Date date = dateField.getDate();
				if(date==null && usedItemHistory==null){
					JOptionPane.showMessageDialog(UsedItemFrame.this,"A Used Date must be set.","Can not save.", JOptionPane.OK_OPTION);
					return;
				}
				
				save(parent,usedItemHistory);
				dispose();
			}
		});
		
		JPanel buttonPanel = new JPanel();
		panel_3.add(buttonPanel, BorderLayout.EAST);
		FlowLayout flowLayout = (FlowLayout) buttonPanel.getLayout();
		flowLayout.setAlignment(FlowLayout.RIGHT);
		
		btnClear = new JButton(usedItemHistory==null?"Clear":"Reset");
		btnClear.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				setTextInFields(usedItemHistory);
			}
		});
		buttonPanel.add(btnClear);
		buttonPanel.add(btnCreateOrUpdate);
		
		setTextInFields(usedItemHistory);
		dateField.setTextEditable(true);
		dateField.getDocument().addDocumentListener(dl);
		dl.changedUpdate(null);
		
		pack();
		setLocationRelativeTo(MainFrame.getInstance());
		setVisible(true);
	}
	
	/**
	 * Add Object specific values that may or may not be optional.
	 * @param parent The object that will have this UsedItemHistory.
	 * @param usedItemHistory The UsedItemHistory to be passed in. Pass in <tt>null</tt> if this is a new UsedItemHistory.
	 */
	protected void createSecondaryValues(final A parent, final B usedItemHistory){
		southPanel = new JPanel();
		panel_2.add(southPanel);
		southPanel.setBorder(new EmptyBorder(0,5,0,5));
		southPanel.setLayout(new GridLayout(0, 2, 5, 5));

		southPanel.add(new JLabel("Cost:"));
		
		cost = new JTextField();
		southPanel.add(cost);
		((AbstractDocument)cost.getDocument()).setDocumentFilter(new DecimalDocumentFilter(Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY));
		cost.setColumns(5);

		southPanel.add(new JLabel("Manufacturer:"));
		
		manuCB = new JComboBox();
		
		Owner[] owners = LivestockSolutions.getOwners();
		String[] strOwnerList = new String[owners.length];
		for(int x=0; x<owners.length; x++){
			strOwnerList[x]=owners[x].getName();
		}

		manuCB.setModel(new DefaultComboBoxModel(strOwnerList));
		manuCB.setEditable(true);
		southPanel.add(manuCB);
	}
	
	/**
	 * Set the text in the fields.
	 * @param usedItemHistory The UsedItemHistory to populate the elements with data from.
	 */
	private void setTextInFields(B usedItemHistory){
		nameCB.setModel(new DefaultComboBoxModel(getList()));
		if(usedItemHistory!=null&&usedItemHistory.getItem()!=null){
			nameCB.setSelectedItem(new UsableItemList<C>((C)usedItemHistory.getItem()));
			dateField.setDate(usedItemHistory.getDate());
			quantity.setText(String.valueOf(usedItemHistory.getQuantity()));
			measurement.setSelectedItem(usedItemHistory.getMeasurement());
		}else{
			nameCB.setSelectedIndex(0);
			dateField.setDate(new Date());
			quantity.setText("0.00");
			measurement.setSelectedItem(0);
		}
		
		// Select the currently item in the list to call the above ActionListener
		nameCB.setSelectedItem(nameCB.getSelectedItem());
		if(measurement.getSelectedIndex()==-1){
			measurement.setSelectedItem(((UsableItemList<C>)measurement.getSelectedItem()).getItem().getMeasurement());
		}
		
		setTextInOptionalFields(usedItemHistory);
	}
	
	/**
	 * Allows additional validation of the data/additional data that may have been added through {@link #createSecondaryValues(A, B)}.<br>
	 * <br>
	 * Sub classes of UsedItemFrame can override this method to save additional data that it may introduce.<br>
	 * To call this method upon updating other Documents attach {@link #dl} to the Document, or call <tt>dl.changedUpdate(DocumentEvent)</tt>
	 * with <tt>null</tt> as the DocumentEvent.
	 * @param isValid Current value of the validation on the Components provided. 
	 *        <tt>true</tt> indicates that the data is valid and saving will be allowed, <tt>false</tt> indicates that at least one
	 *        attribute is invalid and saving will not be allowed (overwritten by returning a value other than <tt>false</tt>). 
	 * @return <tt>true</tt> if data is validated and valid (allow saving), <tt>false</tt> otherwise (do not allow saving).
	 */
	protected abstract boolean doCheck(boolean isValid);
	
	/**
	 * Create a list of items that can be selected in the Item name combo box.
	 * @return A list of items that can be selected in the Item name combo box.
	 */
	protected abstract UsableItemList<C>[] getList();
	
	/**
	 * Sets any values that are not standard across all UsedItemHistory objects.
	 * @param usedItemHistory The UsedItemHistory to get the values from, or <tt>null</tt> for a new UsedItemHistory. 
	 */
	protected void setTextInOptionalFields(B usedItemHistory){
		if(usedItemHistory!=null){
			cost.setText(String.valueOf(usedItemHistory.getCost()));
			if(usedItemHistory.getOrigin()!=null){
				manuCB.setSelectedItem(usedItemHistory.getOrigin().getName());
			}else{
				manuCB.setSelectedIndex(-1);
			}
		}else{
			cost.setText(null);
			manuCB.setSelectedIndex(-1);
		}
	}
	
	/**
	 * Save the usedItemHistory to the given user of the Item
	 * @param parent The user of the item used in the UsedItemHistory
	 * @param usedItemHistory The current value of the UsedItemHistory item, or <tt>null</tt> for a new UsedItemHistory.
	 * @return <tt>true</tt> if the save was successful, <tt>false</tt> otherwise.
	 */
	protected abstract boolean save(A parent, B usedItemHistory);
	
	@Override
	public void dispose(){
		for(WindowListener wl : getWindowListeners()){
			wl.windowClosing(new WindowEvent(this,WindowEvent.WINDOW_CLOSING));
		}
		super.dispose();
	}
	
	@Override
	public void pack(){
		super.pack();
		Dimension d = getSize();
		d.setSize(d.getWidth(),d.getHeight()+10);
		setSize(d);
	}
}